package com.example.lt.service.user.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.lt.constant.RedisConstant;
import com.example.lt.entity.queryPage.BasePage;
import com.example.lt.entity.user.Follow;
import com.example.lt.exception.BaseException;
import com.example.lt.mapper.user.FollowMapper;
import com.example.lt.service.FeedService;
import com.example.lt.service.user.FollowService;
import com.example.lt.service.video.VideoService;
import com.example.lt.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 小涛Tao
 * @Date: 2024/07/29/20:03
 * version 1.0 注释：
 **/
@Service
@RequiredArgsConstructor
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements FollowService {

    private final RedisUtil redisUtil;

    private final FeedService feedService;

    @Lazy // 这里需要用 @Lazy 加 @Autowired 来解决 userServiceImpl、followServiceImpl、videoServiceImpl 这三个 Bean 循环依赖的问题
    @Autowired
    private VideoService videoService;

    private final RedisTemplate redisTemplate;

    /**
     * 关注 或 取关
     * @param userId 用户id
     * @param followsId 目标id
     * @return
     */
    @Override
    public boolean follows(Long userId, Long followsId) {
        if (userId.equals(followsId)) {
            throw new BaseException("你不能关注自己");
        }

        // 这里可以直接创建 Follow 对象直接去保存
        // 因为数据库 follow 表中的 userId字段 和 followId字段 设置了组合的唯一索引
        // 如果数据库中已经存在这条记录了就会插入失败，那就会进行取关操作
        Follow follow = Follow.builder().followId(followsId).userId(userId).build();

        try {
            save(follow); // 往数据库关注表中添加记录
            final Date date = new Date();
            // 往自己的关注列表中添加关注人id
            redisTemplate.opsForZSet().add(RedisConstant.USER_FOLLOW + userId, followsId, date.getTime());
            // 往关注人的粉丝列表中添加自己的id
            redisTemplate.opsForZSet().add(RedisConstant.USER_FANS + followsId, userId, date.getTime());
        } catch (Exception e) {
            // 如果出现异常了，那本次操作就是取关操作
            // 先删除数据库关注表中的记录
            remove(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowId, followsId).eq(Follow::getUserId, userId));

            // 删除收件箱中的视频，先获取关注人所有公开且审核通过的视频id集合
            final List<Long> videoIds = videoService.listVideoIdByUserId(followsId); // 获取视频id集合
            feedService.deleteInBoxFeed(userId, videoIds); // 删除收件箱中的视频

            // 删除自己关注列表中的关注人id
            redisTemplate.opsForZSet().remove(RedisConstant.USER_FOLLOW + userId, followsId);
            // 删除关注人粉丝列表中自己的id
            redisTemplate.opsForZSet().remove(RedisConstant.USER_FANS + followsId, userId);

            return false; // 标识这次操作是取关操作
        }

        return true; // 标识这次操作是关注操作
    }

    /**
     * 获取当前用户的关注数量
     * @param userId
     * @return
     */
    @Override
    public int getFollowCount(Long userId) {
        return count(new LambdaQueryWrapper<Follow>().eq(Follow::getUserId, userId));
    }

    /**
     * 获取当前用户的粉丝数量
     * @param userId
     * @return
     */
    @Override
    public int getFansCount(Long userId) {
        return count(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowId, userId));
    }

    /**
     * 获取关注人员的id，并且按照关注时间排序，关注越晚排在越前，倒序排序
     * @param userId 用户id
     * @param basePage 分页信息
     * @return
     */
    @Override
    public Collection<Long> getFollow(Long userId, BasePage basePage) {
        // 如果 basePage 是空，那就是获取所有的关注人员id，用于初始化收件箱，或其他
        // 如果 basePage 不为空，那就是根据 page 值来获取
        if (basePage == null) {
            // 从 redis 中获取
            final Set<Object> set = redisUtil.zGet(RedisConstant.USER_FOLLOW + userId);
            if (ObjectUtils.isEmpty(set)) {
                return Collections.EMPTY_SET;
            }
            return set.stream().map(o -> Long.valueOf(o.toString())).collect(Collectors.toList());
        }
        // 从 redis 中获取了指定 key 和范围的【元素及其对应分数】的集合
        final Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisUtil.zSetGetByPage(RedisConstant.USER_FOLLOW + userId, basePage.getPage(), basePage.getLimit());
        // 判断有没有从 redis 中拿到数据，没有的话可能是 redis 崩了，需要去 db 中获取
        if (ObjectUtils.isEmpty(typedTuples)) {
            final List<Follow> follows = page(basePage.page(), new LambdaQueryWrapper<Follow>().eq(Follow::getUserId, userId).orderByDesc(Follow::getGmtCreated)).getRecords();
            if (ObjectUtils.isEmpty(follows)) {
                return Collections.EMPTY_LIST;
            }
            return follows.stream().map(Follow::getFollowId).collect(Collectors.toList()); // 提取被关注人id
        }
        // 把 typedTuples 中的值提取出来转换为 long 值，存放到 List 集合中
        return typedTuples.stream().map(t -> Long.parseLong(t.getValue().toString())).collect(Collectors.toList());
    }

    /**
     * 获取粉丝的id，并按照关注时间排序，关注越晚排在越前，倒序排序
     * 分为获取 userId 的所有粉丝，和分页获取 userId 的粉丝
     * @param userId 用户id
     * @param basePage 为 null 时获取全部，否则分页获取
     * @return 粉丝的 id 集合
     */
    @Override
    public Collection<Long> getFans(Long userId, BasePage basePage) {
        if (basePage == null) {
            final Set<Object> set = redisUtil.zGet(RedisConstant.USER_FANS + userId);
            if (ObjectUtils.isEmpty(set)) {
                return Collections.EMPTY_SET; // Collections.EMPTY_SET 是 Java 集合框架中的一个静态常量，代表一个不可变的空集合
            }
            // 把 id 都转换成 long 类型
            return set.stream().map(o -> Long.valueOf(o.toString())).collect(Collectors.toList());
        }
        // 从 redis 中获取了指定 key 和 范围 的【元素及其对应分数】的集合
        final Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisUtil.zSetGetByPage(RedisConstant.USER_FANS + userId, basePage.getPage(), basePage.getLimit());
        // 判断有没有从 redis 中拿到数据，没有的话可能是 redis 崩了，需要去 db 中获取
        if (ObjectUtils.isEmpty(typedTuples)) {
            final List<Follow> fans = page(basePage.page(), new LambdaQueryWrapper<Follow>().eq(Follow::getFollowId, userId)).getRecords();
            if (ObjectUtils.isEmpty(fans)) {
                return Collections.EMPTY_LIST;
            }
            return fans.stream().map(Follow::getUserId).collect(Collectors.toList()); // 提取粉丝的id
        }
        // 把 typedTuples 中的值提取出来转换为 long 值，存放到 List 集合中
        return typedTuples.stream().map(t -> Long.parseLong(t.getValue().toString())).collect(Collectors.toList());
    }

    /**
     * userId 是否关注 followId
     * @param followId 被关注者
     * @param userId 用户
     * @return
     */
    @Override
    public Boolean isFollows(Long followId, Long userId) {
        if (userId == null || followId == null) return false;
        return count(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowId,followId).eq(Follow::getUserId,userId)) == 1;
    }

}
